/*
  Copyright (C) 2011, Kyungjoo Kim
  
  This file is part of LINAL (LINear ALgebra)
  
  All rights reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.
  
  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.
  
  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  Also add information on how to contact you by electronic and paper mail.
  
  
  Kyungjoo Kim
  iamkyungjoo@gmail.com
*/
#include "linal/common.hxx"
#include "linal/util.hxx"

#include "linal/format.hxx"

#include "linal/operation.hxx"

#include "linal/device/host/operation.hxx"

namespace linal {

  static int apply_q_l( int trans, int direct,
                        Hier_ A, Hier_ T, Hier_ W, Hier_ B );

  int apply_q_l( int trans, int direct, 
                 Hier_ A, Hier_ T, Hier_ W, Hier_ B ) {
    Flat_ A_flat = A.flat(), T_flat = T.flat();

    FLA_Obj BL,    BR,       B0,  B1,  B2;
    FLA_Obj WL,    WR,       W0,  W1,  W2;

    FLA_Part_1x2(~B,    &BL,  &BR,      0, FLA_LEFT );
    FLA_Part_1x2(~W,    &WL,  &WR,      0, FLA_LEFT );
    
    while ( FLA_Obj_width( BL ) < FLA_Obj_width(~B ) ){
      FLA_Repart_1x2_to_1x3( BL,   BR,        &B0,  &B1, &B2,
                             1, FLA_RIGHT );

      FLA_Repart_1x2_to_1x3( WL,   WR,        &W0,  &W1, &W2,
                             1, FLA_RIGHT );

      //------------------------------------------------------------
      Hier_ W_1(W1), B_1(B1);

#pragma omp task firstprivate( W_1, B_1 ) 
      FLA_Apply_Q_UT( FLA_LEFT, trans,
                      direct, FLA_COLUMNWISE,
                      ~A_flat, ~T_flat, ~W_1.flat(), ~B_1.flat() );
                        
      //------------------------------------------------------------
      FLA_Cont_with_1x3_to_1x2( &BL,   &BR,        B0, B1,  B2,
                                FLA_LEFT );

      FLA_Cont_with_1x3_to_1x2( &WL,   &WR,        W0, W1,  W2,
                                FLA_LEFT );
    }

#pragma omp taskwait

    return true;
  }
    
  int apply_q(int side, int trans, int direct, int storev,
              Hier_ A, Hier_ T, Hier_ W, Hier_ B) {

    switch (side) {
    case FLA_LEFT: 
      {
        apply_q_l( trans, direct, A, T, W, B );
        break;
      }
    case FLA_RIGHT:
      {
        break;
      }
    }
    return true;
  }
}
